package com.gking.resourcePool.utils;


import com.gking.resourcePool.utils.commoUtil.CustomEncryptorUtil;
import com.gking.resourcePool.utils.commoUtil.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;

import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;

import static com.gking.resourcePool.common.RedisConstants.*;

/**
 * 通过 HttpServletRequest 获取登录用户相关信息
 */
@Slf4j
public class GetAuthInfoUtil {


    private final StringRedisTemplate stringRedisTemplate;

    private final HttpServletRequest request;

    public GetAuthInfoUtil(StringRedisTemplate stringRedisTemplate, HttpServletRequest request) {
        this.stringRedisTemplate = stringRedisTemplate;
        this.request = request;
    }


    /**
     * 通过用户的登录token获取用户token中的明文信息
     *
     * @return 登录用户的 userKey
     */
    public String getTokenUserKey() {
        String token = request.getHeader("Authorization");
        return CustomEncryptorUtil.decrypt(JwtUtil.verifyToken(token));
    }

    /**
     * 通过用户的登录token获取用户token中的明文信息
     *
     * @return 登录用户的 userKey
     */
    public static String getTokenUserKey(String token) {
        try {
            return CustomEncryptorUtil.decrypt(JwtUtil.verifyToken(token));
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 验证登录用户
     *
     * @return true: 验证通过 false: 验证失败
     */
    public boolean verifyLoginUser() {
        String userServerInfo = getUserServerInfo();
        String userDeviceInfo = getUserDeviceInfo();
        String userLoginIp = getUserLoginIp();

        String clientIp = getClientIp(request);
        String clientDeviceInfo = getClientDeviceInfo(request);

        // 存储在服务端的用户信息是否存在
        if (userServerInfo == null || userLoginIp == null || userDeviceInfo == null) return false;
        // 当前登录用户设备id和登录时记录的设备id是否一致，防止token盗取（访问设备一致性）
        if (!userDeviceInfo.equals(clientDeviceInfo)) return false;
        // 当前登录用户访问IP和登录时记录的访问IP是否一致，防止token盗取（访问IP一致性）
        if (!userLoginIp.equals(clientIp)) return false;

        return true;
    }

    /**
     * 清空服务端登录用户缓存信息
     */
    public void clearUserCacheInfo() {
        String userKey = getTokenUserKey();

        stringRedisTemplate.delete(Login_User_Server_Key + userKey);
        stringRedisTemplate.delete(Login_User_Equipment + userKey);
        stringRedisTemplate.delete(Login_User_Ip + userKey);
    }

    /**
     * 重置用户服务端登录信息（服务端用户信息保活 keep-alive）
     */
    public void keepAliveUserServerInfo() {
        String userKey = getTokenUserKey();

        String userServerInfo = getUserServerInfo();
        String userDeviceInfo = getUserDeviceInfo();
        String userLoginIp = getUserLoginIp();

        setUserServerInfo(userKey, userServerInfo);
        setUserDeviceInfo(userKey, userDeviceInfo);
        setUserLoginIp(userKey, userLoginIp);
    }

    /**
     * 获取客户端token中携带的验证吗
     *
     * @return token携带的验证码
     */
    public String getUserVerifyCode() {
        try {
            String userKey = getTokenUserKey();
            // userKey 前4位为当前用户登录时的验证码
            assert userKey != null;
            return userKey.substring(0, 4);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 通过用户的登录token获取用户服务端登录用户信息
     *
     * @param token token
     * @return 用户id
     */
    public String getUserServerInfoByToken(String token) {
        String userKey = CustomEncryptorUtil.decrypt(JwtUtil.verifyToken(token));
        return stringRedisTemplate.opsForValue().get(Login_User_Server_Key + userKey);
    }

    /**
     * 用户登录时设置服务端登录用户信息
     *
     * @param userKey  用户索引键
     * @param userInfo 用户信息
     */
    public void setUserServerInfo(String userKey, String userInfo) {
        stringRedisTemplate.opsForValue().set(
                Login_User_Server_Key + userKey,
                userInfo,
                20L,
                TimeUnit.MINUTES);
    }

    /**
     * 通过用户的登录token获取用户服务端登录用户信息
     *
     * @return 用户id
     */
    public String getUserServerInfo() {
        String userKey = getTokenUserKey();
        return stringRedisTemplate.opsForValue().get(Login_User_Server_Key + userKey);
    }


    /**
     * 用户登录时设置服务端登录用户设备信息
     *
     * @param userKey    用户索引键
     * @param deviceInfo 设备信息
     */
    public void setUserDeviceInfo(String userKey, String deviceInfo) {
        try {
            stringRedisTemplate.opsForValue().set(
                    Login_User_Equipment + userKey,
                    deviceInfo,
                    20L,
                    TimeUnit.MINUTES);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 通过用户的登录token获取服务端用户设备id
     *
     * @return 用户登录设备id
     */
    public String getUserDeviceInfo() {
        String userKey = getTokenUserKey();
        return stringRedisTemplate.opsForValue().get(Login_User_Equipment + userKey);
    }


    /**
     * 用户登录时设置服务端登录用户ip
     *
     * @param userKey 用户索引键
     */
    public void setUserLoginIp(String userKey, String ip) {
        stringRedisTemplate.opsForValue().set(
                Login_User_Ip + userKey,
                ip,
                20L,
                TimeUnit.MINUTES);
    }

    /**
     * 通过用户的登录token获取用户服务端登录用户ip
     *
     * @return 用户登录存储的IP
     */
    public String getUserLoginIp() {
        String userKey = getTokenUserKey();
        return stringRedisTemplate.opsForValue().get(Login_User_Ip + userKey);
    }


    /**
     * 获取客户端ip地址，支持获取通过nginx代理服务器设置的真实IP
     *
     * @param request 请求
     * @return 客户端ip地址
     */
    public static String getClientIp(HttpServletRequest request) {

//        Enumeration<String> headerNames = request.getHeaderNames();
//        while (headerNames.hasMoreElements()) {
//            String headerName = headerNames.nextElement();
//            log.info("Header: {} = {}", headerName, request.getHeader(headerName));
//        }

        String remoteAddr = "";

        if (request != null) {
            remoteAddr = request.getHeader("X-Forwarded-For");
            if (remoteAddr == null || remoteAddr.isEmpty() || "unknown".equalsIgnoreCase(remoteAddr)) {
                remoteAddr = request.getHeader("X-Real-IP");
            }
            if (remoteAddr == null || remoteAddr.isEmpty() || "unknown".equalsIgnoreCase(remoteAddr)) {
                remoteAddr = request.getRemoteAddr();
            }
        }

        return remoteAddr;
    }

    /**
     * 获取客户端设备信息
     *
     * @param request 请求
     * @return 客户端设备信息
     */
    public static String getClientDeviceInfo(HttpServletRequest request) {
        try {
            return request.getHeader("c-device-id");
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取客户端token中携带的验证吗
     *
     * @param token token
     * @return token携带的验证码
     */
    public static String getClientVerifyCode(String token) {
        try {
            String userKey = CustomEncryptorUtil.decrypt(JwtUtil.verifyToken(token));
            // userKey 前4位为当前用户登录时的验证码
            assert userKey != null;
            return userKey.substring(0, 4);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 获取前端请求携带的用户id
     *
     * @param token token
     * @return s
     */
    public static String getUserId(String token) {
        String decrypt = CustomEncryptorUtil.decrypt(JwtUtil.verifyToken(token));
        try {
            assert decrypt != null;
            return decrypt.split(";code_")[0];
        } catch (Exception e) {
            log.error("获取用户id失败");
            return null;
        }
    }

    /**
     * 获取前端请求携带的用户id
     *
     * @param token token
     * @return s
     */
    public static String getVerifyCode(String token) {
        String decrypt = CustomEncryptorUtil.decrypt(JwtUtil.verifyToken(token));
        try {
            assert decrypt != null;
            return decrypt.split(";code_")[1];
        } catch (Exception e) {
            log.error("获取用户登录验证码失败");
            return null;
        }
    }
}
